using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using DataAccess;
using DataAccess.BusinessEntities;
using Locator.GeoLocation.BusinessEntities;
using Locator.GeoLocation.Services;
using Microsoft.Practices.CompositeWeb;
using Microsoft.Practices.CompositeWeb.Interfaces;
using Microsoft.Practices.CompositeWeb.Utility;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.ObjectBuilder;
using DataAccess.Services;


namespace Locator.PersonalLocations
{
    public class PersonalLocationsController : IPersonalLocationsController
    {
        private readonly TraceManager traceManager;
        private readonly LogWriter logWriter;
        private bool disposed;
        private readonly IGeoLocationServiceAgent geoLocationService;
        private readonly IDomainContext domainContext;

        /// <summary>
        /// Initializes a new instance of the <see cref="PersonalLocationsController"/> class.
        /// </summary>
        public PersonalLocationsController
            (
            [CreateNew] IDomainContext domainContext,
            [ServiceDependency] IGeoLocationServiceAgent geoLocationService
            )
        {
            this.logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
            this.traceManager = new TraceManager(this.logWriter);
            this.geoLocationService = geoLocationService;
            this.domainContext = domainContext;
        }

        /// <summary>
        /// Gets the personal locations for current user.
        /// </summary>
        /// <returns></returns>
        public List<PersonalLocation> GetPersonalLocationsForCurrentUser()
        {
            List<PersonalLocation> result = new List<PersonalLocation>();
            User user = this.GetUserEntity();
            result.AddRange(user.PersonalLocations);

            return result;
        }

        /// <summary>
        /// Gets a personal location for the current user.
        /// </summary>
        /// <param name="Id">The ID.</param>
        /// <returns></returns>
        public PersonalLocation GetPersonalLocationForCurrentUser(int Id)
        {
            PersonalLocation result = null;
            User user = GetUserEntity();
            result = user.PersonalLocations.FirstOrDefault(p => p.Id == Id);

            return result;
        }

        /// <summary>
        /// Adds a personal location for the current user.
        /// </summary>
        /// <param name="description">The description.</param>
        /// <param name="latitude">The latitude.</param>
        /// <param name="longitude">The longitude.</param>
        public void AddPersonalLocationForCurrentUser(string description, double latitude, double longitude)
        {
            var user = this.GetUserEntity();
            PersonalLocation location = new PersonalLocation() { Latitude = (decimal)latitude, Longitude = (decimal)longitude, Description = description };
            this.domainContext.Save<PersonalLocation>(location);
            //            this.unitOfWork.PersonalLocations.Add(location);
            user.PersonalLocations.Add(location);
            //this.domainContext.Save<User>(user);
            //this.unitOfWork.Commit();
            this.domainContext.SaveChanges();
        }

        /// <summary>
        /// Deletes a personal location for the current user.
        /// </summary>
        /// <param name="personalLocationId">The personal location id.</param>
        public void DeletePersonalLocationForCurrentUser(int personalLocationId)
        {
            var user = this.GetUserEntity();
            //PersonalLocation location = this.unitOfWork.PersonalLocations.FindById(personalLocationId);
            PersonalLocation location = this.domainContext.PersonalLocations.Single(pl => pl.Id == personalLocationId);
            if (location != null)
            {
                user.PersonalLocations.Remove(location);
                //this.unitOfWork.Commit();
                this.domainContext.SaveChanges();
            }
        }

        /// <summary>
        /// Searches for geo coordinates of an address.
        /// </summary>
        /// <param name="streetAddress">The street address.</param>
        /// <param name="country">The country.</param>
        /// <param name="province">The province.</param>
        /// <param name="city">The city.</param>
        /// <returns></returns>
        public Location SearchByAddress(string streetAddress, string country, string province, string city)
        {
            var location = this.geoLocationService.GeoLocate(new GeoLocation.BusinessEntities.Address()
            {
                AddressLine = streetAddress,
                CountryRegion = country,
                AdminDistrict = province,
                Locality = city
            });

            return location;
        }

        /// <summary>
        /// Searches for geo coordinates of a postal code.
        /// </summary>
        /// <param name="postalCode">The postal code.</param>
        /// <param name="country">The country.</param>
        /// <returns></returns>
        public Location SearchByPostalCode(string postalCode, string country)
        {
            var location = this.geoLocationService.GeoLocate(new GeoLocation.BusinessEntities.Address()
            {
                CountryRegion = country,
                PostalCode = postalCode
            });

            return location;
        }

        /// <summary>
        /// Gets the user entity.
        /// </summary>
        /// 
        /// <returns></returns>
        private User GetUserEntity()
        {
            string userName = Thread.CurrentPrincipal.Identity.Name;
            User result = null;

            var userEntities2 = this.domainContext.Users.Where(u => String.Compare(u.Username, userName, true) == 0).Include(u => u.PersonalLocations);
            if (userEntities2.Count() == 0)
            {
                User newUser = new User() { Username = userName };
                this.domainContext.Save<User>(newUser);
                this.domainContext.SaveChanges();
                newUser = this.domainContext.Users.Where(u => u.Username == userName).Include(u => u.PersonalLocations).First();
                result = newUser;
                //                throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "Couldn't find a user with user name {0} and ID {1}", userName, guid));
            }
            else
            {
                result = userEntities2.First();
            }

            return result;
        }

        public virtual void AddPersonalLocations()
        {
            this.RedirectTo("AddPersonalLocations.aspx");
        }

        protected virtual void RedirectTo(string address)
        {
            Guard.ArgumentNotNullOrEmptyString(address, "address");
            IHttpContext context = new Microsoft.Practices.CompositeWeb.Web.HttpContext(HttpContext.Current);
            context.Server.Transfer(address);
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    if (this.domainContext != null)
                    {
                        this.domainContext.Dispose();
                    }

                    if (this.logWriter != null)
                    {
                        this.logWriter.Dispose();
                    }
                }
            }

            this.disposed = true;
        }

        ~PersonalLocationsController()
        {
            this.Dispose(false);
        }
    }
}
